package com.jiangyg.mall.authz.support.authentication.admin;

import com.jiangyg.mall.authz.constant.SecurityConstant;
import com.jiangyg.mall.authz.support.authentication.AuthenticationRequestMatcher;
import com.jiangyg.mall.authz.support.authentication.exception.AdminAuthenticationException;
import com.jiangyg.mall.core.utils.Assert;
import com.jiangyg.mall.core.utils.JsonUtils;
import com.jiangyg.mall.core.utils.WebUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.web.filter.GenericFilterBean;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 类描述：后台管理访问权限校验
 *
 * @author jiangyg
 * @date 2022-09-03
 */
@Slf4j
public class AdminAccessAuthenticationFilter extends GenericFilterBean {

    /**
     * 验证管理器
     */
    private final AuthenticationManager authenticationManager;

    /**
     * 请求地址匹配器，此过滤器只处理此请求
     */
    private final AuthenticationRequestMatcher matcher = new AuthenticationRequestMatcher();

    public AdminAccessAuthenticationFilter(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
        // 1. 请求类型判断
        if (!(servletRequest instanceof HttpServletRequest) || !(servletResponse instanceof HttpServletResponse)) {
            throw new ServletException("TokenAuthenticationFilter 仅支持 HTTP 请求！");
        }
        final HttpServletRequest request = (HttpServletRequest) servletRequest;
        final HttpServletResponse response = (HttpServletResponse) servletResponse;
        // 2. 判断是否为 authentication 请求，如果不是则退出
        if (!requiresAuthentication(request)) {
            chain.doFilter(request, response);
            return;
        }
        // 3. 获取待校验的请求地址
        final String authRequestPath = request.getParameter("path");
        if (StringUtils.isBlank(authRequestPath)) {
            throw new AdminAuthenticationException("没有匹配需要校验的path！");
        }
        // 4. 认证用户信息
        final String token = WebUtils.attemptReadRequestArgument(SecurityConstant.tokenName(), request);
        // 5. 执行验证
        final AdminAccessAuthentication authentication = new AdminAccessAuthentication(token, authRequestPath);
        this.authenticationManager.authenticate(authentication);
        // 6. 执行此处说明验证通过，获取认证信息并加密
        final Object userInfo = authentication.getDetails();
        Assert.notNull(userInfo);
        final String userInfoString = JsonUtils.toJSONString(userInfo);
        request.setAttribute(SecurityConstant.AUTH_INFO_ATTR_NAME, Base64.encodeBase64String(userInfoString.getBytes()));
        // 7. 继续执行过滤器
        chain.doFilter(request, response);
    }

    /**
     * 功能描述：判断请求是否满足匹配规则
     *
     * @param request 请求
     * @return 是否
     */
    private boolean requiresAuthentication(HttpServletRequest request) {
        return matcher.matches(request);
    }

}
